﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Facebook.Api;
using System.Collections;

namespace Facebook.Web
{
    [Serializable]
    public class FacebookWebSession : SessionInfo, ISessionInfo, IDictionary<String, Object>
    {
        public FacebookWebSession(HttpContext context)
        {
            this.Merge(context);
        }

        public void Merge(HttpContext context)
        {
            Byte added;
            if (Byte.TryParse(context.Request.Params["fb_sig_added"], out added)) this.Added = added == 1;

            String apiKey = context.Request.Params["fb_sig_api_key"];
            if (!String.IsNullOrEmpty(apiKey)) this.ApiKey = apiKey;

            String strFriends = context.Request.Params["fb_sig_friends"];
            if (!String.IsNullOrEmpty(strFriends)) this.Friends = new List<Int64>(
                 from strFriend in strFriends.Split(',')
                 select Int64.Parse(strFriend));

            String locale = context.Request.Params["fb_sig_locale"];
            if (!String.IsNullOrEmpty(locale)) this.Locale = locale;

            Int64 uid;
            if (!Int64.TryParse(context.Request.Params["fb_sig_user"], out uid))
                if (!Int64.TryParse(context.Request.Params["fb_sig_canvas_user"], out uid))
                    Int64.TryParse(context.Request.Params["fb_sig_profile_user"], out uid);
            if (uid > 0) this.Uid = uid;

            String strLinkedAccounts = context.Request.Params["fg_sig_linked_account_ids"];
            if (!String.IsNullOrEmpty(strLinkedAccounts)) this.Friends = new List<Int64>(
                 from strLinkedAccount in strLinkedAccounts.Split(',')
                 select Int64.Parse(strLinkedAccounts));

            String sessionKey = context.Request.Params["fb_sig_session_key"];
            if (!String.IsNullOrEmpty(sessionKey)) this.SessionKey = sessionKey;

            UnixDateTime expires;
            if (context.Request.Params["fb_sig_expires"] == "0") this.Expires = DateTime.MaxValue;
            else if (UnixDateTime.TryParse(context.Request.Params["fb_sig_expires"], out expires)) this.Expires = expires.ToDateTime();

            UnixDateTime lastProfileUpdate;
            if (UnixDateTime.TryParse(context.Request.Params["fb_sig_profile_update_time"], out lastProfileUpdate)) this.LastProfileUpdate = lastProfileUpdate.ToDateTime();

            String extPerms = context.Request.Params["fb_sig_ext_perms"];
            if (!String.IsNullOrEmpty(extPerms)) this.ExtendedPermissions = new List<String>(extPerms.Split(','));
        }

        protected internal FacebookWebSession(IDictionary<String, Object> dictionary)
            : base(dictionary) { }

        protected internal static String SessionCacheKey
        {
            get { return String.Format("__FBAPI__SESSION__{0}", FacebookWebRequest.Current.Uid); }
        }

        public static FacebookWebSession Current
        {
            get { return (FacebookWebSession)HttpRuntime.Cache[FacebookWebSession.SessionCacheKey]; }
            protected internal set { HttpRuntime.Cache[FacebookWebSession.SessionCacheKey] = value; }
        }

        public static void Init(HttpContext context)
        {
            if (FacebookWebSession.Current == null) FacebookWebSession.Current = new FacebookWebSession(context);
            else FacebookWebSession.Current.Merge(context);
        }

        public Boolean Added { get; set; }
        public String ApiKey { get; set; }
        public List<Int64> Friends { get; set; }
        public String Locale { get; set; }
        public List<Int64> LinkedAccountIds { get; set; }

        public DateTime LastProfileUpdate { get; set; }
        public List<String> ExtendedPermissions { get; set; }

        private const String SESSION_PREFIX = "__SESSION__";
        private String GetInternalKey(String key)
        {
            return SESSION_PREFIX + key;
        }

        #region [ IDictionary<String, Object> Members ]

        public void Add(String key, Object value)
        {
            this.InnerDictionary.Add(this.GetInternalKey(key), value);
        }

        public Boolean ContainsKey(String key)
        {
            return this.InnerDictionary.ContainsKey(this.GetInternalKey(key));
        }

        public ICollection<String> Keys
        {
            get { return this.InnerDictionary.Keys.Where(key => key.StartsWith(SESSION_PREFIX)).ToList(); }
        }

        public Boolean Remove(String key)
        {
            return this.InnerDictionary.Remove(this.GetInternalKey(key));
        }

        public Boolean TryGetValue(String key, out Object value)
        {
            return this.InnerDictionary.TryGetValue(this.GetInternalKey(key), out value);
        }

        public ICollection<Object> Values
        {
            get { return this.InnerDictionary.Where(kvp => kvp.Key.StartsWith(SESSION_PREFIX)).Select(kvp => kvp.Value).ToList(); }
        }

        public Object this[String key]
        {
            get
            {
                Object value;
                this.TryGetValue(key, out value);
                return value;
            }
            set { this.InnerDictionary[this.GetInternalKey(key)] = value; }
        }

        #endregion

        #region [ ICollection<KeyValuePair<String, Object>> Members ]

        void ICollection<KeyValuePair<String, Object>>.Add(KeyValuePair<String, Object> item)
        {
            this.Add(item.Key, item.Value);
        }

        public void Clear()
        {
            while (this.InnerDictionary.Keys.Any(key => key.StartsWith(SESSION_PREFIX)))
            {
                this.InnerDictionary.Remove(this.InnerDictionary.Keys.First(key => key.StartsWith(SESSION_PREFIX)));
            }
        }

        Boolean ICollection<KeyValuePair<String, Object>>.Contains(KeyValuePair<String, Object> item)
        {
            return this.InnerDictionary.Contains(new KeyValuePair<String, Object>(this.GetInternalKey(item.Key), item.Value));
        }

        void ICollection<KeyValuePair<String, Object>>.CopyTo(KeyValuePair<String, Object>[] array, Int32 arrayIndex)
        {
            this.InnerDictionary.Where(kvp => kvp.Key.StartsWith(SESSION_PREFIX)).ToArray().CopyTo(array, arrayIndex);
        }

        public Int32 Count
        {
            get { return this.InnerDictionary.Keys.Where(key => key.StartsWith(SESSION_PREFIX)).Count(); }
        }

        Boolean ICollection<KeyValuePair<String, Object>>.IsReadOnly
        {
            get { return false; }
        }

        Boolean ICollection<KeyValuePair<String, Object>>.Remove(KeyValuePair<String, Object> item)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region [ IEnumerable<KeyValuePair<string,object>> Members ]

        IEnumerator<KeyValuePair<String, Object>> IEnumerable<KeyValuePair<String, Object>>.GetEnumerator()
        {
            return this.InnerDictionary.Where(kvp => kvp.Key.StartsWith(SESSION_PREFIX)).GetEnumerator();
        }

        #endregion

        #region [ IEnumerable Members ]

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.InnerDictionary.Where(kvp => kvp.Key.StartsWith(SESSION_PREFIX)).GetEnumerator();
        }

        #endregion
    }
}
